#include <erl_nif.h>
#include <opus/opusfile.h>
#include <opus/opusenc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义错误处理宏
#define CHECK_NULL(ptr, msg) \
    if (!(ptr)) { \
        fprintf(stderr, "Error: %s\n", msg); \
        return NULL; \
    }

#define CHECK_ERROR(cond, msg) \
    if (!(cond)) { \
        fprintf(stderr, "Error: %s\n", msg); \
        return -1; \
    }

// 创建 OGG 编码器
static OggOpusEnc* create_ogg_encoder(ErlNifEnv* env, ErlNifBinary* output, int sample_rate, int channels) {
    int error = 0;
    OggOpusEnc* enc = ope_encoder_create_pull(NULL, sample_rate, channels, 0, &error);
    CHECK_NULL(enc, "Failed to create OGG encoder");
    return enc;
}


// 创建 Opus 编码器
static OggOpusEnc* create_opus_encoder(ErlNifEnv* env, ErlNifBinary* output, int sample_rate, int channels) {
    int error = 0;
    OggOpusEnc* enc = ope_encoder_create_pull(NULL, sample_rate, channels, 0, &error);
    CHECK_NULL(enc, "Failed to create Opus encoder");
    return enc;
}


// 编码 OPUS 二进制流到 OGG 流
static int opus_to_ogg(ErlNifEnv* env, ErlNifBinary* opus_data, ErlNifBinary* output) {
    OggOpusFile* of = op_open_memory(opus_data->data, opus_data->size, 0);
    CHECK_NULL(of, "Failed to open OPUS file");

    int channels = 1;
    int sample_rate = 16000; // 默认采样率

    OggOpusEnc* enc = create_ogg_encoder(env, output, sample_rate, channels);
    CHECK_NULL(enc, "Failed to create OGG encoder");

    ogg_packet op;
    op.packet = (unsigned char*)opus_data->data;
    op.bytes = opus_data->size;
    op.b_o_s = 1;
    op.e_o_s = 0;
    op.granulepos = 0;
    op.packetno = 0;

    int ret = ope_encoder_write(enc, (const opus_int16*)op.packet, op.bytes / 2);
    CHECK_ERROR(ret == 0, "Failed to write data to OGG encoder");

    op_free(of);
    ope_encoder_drain(enc);
    ope_encoder_destroy(enc);

    return 0;
}


// 编码 Ogg 二进制流到 Opus 流
static int ogg_to_opus(ErlNifEnv* env, ErlNifBinary* ogg_data, ErlNifBinary* output) {
    OggOpusFile* of = op_open_memory(ogg_data->data, ogg_data->size, 0);
    CHECK_NULL(of, "Failed to open OGG file");

    // int channels = op_head_get_input_sample_rate(op_head(of, -1));
    // int sample_rate = 48000; // 默认采样率

    int channels = 1;
    int sample_rate = 16000;

    OggOpusEnc* enc = create_opus_encoder(env, output, sample_rate, channels);
    CHECK_NULL(enc, "Failed to create Opus encoder");

    opus_int16* pcm_buffer = malloc(ogg_data->size * 2);
    CHECK_NULL(pcm_buffer, "Failed to allocate PCM buffer");

    int samples_read = op_read_stereo(of, pcm_buffer, ogg_data->size / 2);
    CHECK_ERROR(samples_read > 0, "Failed to read OGG data");

    int ret = ope_encoder_write(enc, pcm_buffer, samples_read);
    CHECK_ERROR(ret == 0, "Failed to write data to Opus encoder");

    op_free(of);
    ope_encoder_drain(enc);
    ope_encoder_destroy(enc);
    free(pcm_buffer);

    return 0;
}


// 定义 NIF 函数
static ERL_NIF_TERM opus_trans_ogg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifBinary opus_bin;
    ErlNifBinary output_bin;

    // 获取输入的 OPUS 二进制流
    if (!enif_inspect_binary(env, argv[0], &opus_bin)) {
        return enif_make_badarg(env);
    }

    // 初始化输出的 OGG 二进制流
    output_bin.data = malloc(opus_bin.size * 2);  // 假设输出大小不会超过输入的两倍
    output_bin.size = opus_bin.size * 2;

    // 编码 OPUS 流到 OGG 流
    if (opus_to_ogg(env, &opus_bin, &output_bin) != 0) {
        free(output_bin.data);
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, "Failed to transcode OPUS to OGG", ERL_NIF_LATIN1));
    }

    // 返回 OGG 二进制流
    ERL_NIF_TERM output_term = enif_make_binary(env, &output_bin);
    free(output_bin.data);
    return enif_make_tuple2(env, enif_make_atom(env, "ok"), output_term);
}



// 定义 NIF 函数
static ERL_NIF_TERM ogg_trans_opus(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifBinary ogg_bin;
    ErlNifBinary output_bin;

    // 获取输入的 Ogg 二进制流
    if (!enif_inspect_binary(env, argv[0], &ogg_bin)) {
        return enif_make_badarg(env);
    }

    // 初始化输出的 Opus 二进制流
    output_bin.data = malloc(ogg_bin.size * 2);  // 假设输出大小不会超过输入的两倍
    output_bin.size = ogg_bin.size * 2;

    // 编码 Ogg 流到 Opus 流
    if (ogg_to_opus(env, &ogg_bin, &output_bin) != 0) {
        free(output_bin.data);
        return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, "Failed to transcode Ogg to Opus", ERL_NIF_LATIN1));
    }

    // 返回 Opus 二进制流
    unsigned char* output_data;
    ERL_NIF_TERM output_term;
    output_data = enif_make_new_binary(env, output_bin.size, &output_term);
    memcpy(output_data, output_bin.data, output_bin.size);
    free(output_bin.data);
    return enif_make_tuple2(env, enif_make_atom(env, "ok"), output_term);
}


// NIF 初始化函数
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
    return 0;
}

// NIF 卸载函数
static void unload(ErlNifEnv* env, void* priv_data) {
}

// NIF 函数表
static ErlNifFunc nif_funcs[] = {
    {"opus_trans_ogg", 1, opus_trans_ogg},
    {"ogg_trans_opus", 1, ogg_trans_opus}
};

// NIF 模块定义
ERL_NIF_INIT(Elixir.SockWorkstation.OpusVsOggNif, nif_funcs, &load, NULL, NULL, &unload)